package com.ctrlspace.core.managers;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import com.ctrlspace.api.IProjectNature;
import com.ctrlspace.core.compiler.IJavaCompiler;
import com.ctrlspace.core.dao.ProjectDao;
import com.ctrlspace.core.util.Security;
import com.ctrlspace.model.FileUnit;
import com.ctrlspace.model.ProjectUnit;
import com.google.appengine.AppEngineProject;
import com.ctrlspace.core.compiler.Compiler;

public class ProjectManager {
	
	private final ProjectDao dao = new ProjectDao();
    private static ProjectManager _instance;
    
    private ProjectManager() {}
    public static ProjectManager getInstance() {
            if (_instance == null) {
                    _instance = new ProjectManager();
            }
            
            return _instance;
    }

	public ProjectUnit createProject(int userId, String title, String description, String projectNatureString, Map<?, ?> options) throws Exception {
		// First, create project internally before calling createProject on nature
		// Determine new path
		String path = Security.SHA1(userId + new Date().toString() + title);
		
		// Create project internally
		ProjectUnit projectUnit = dao.createProject(userId, title, description, path, projectNatureString);
		
		// Call nature's createProject, which may or may not updated projectUnit
		// nature's createProject should be responsible for creating the file system representation
		IProjectNature project = (IProjectNature) Class.forName(projectNatureString).newInstance();
		ProjectUnit modifiedProjectUnit = null;
		try {
			modifiedProjectUnit = project.createProject(projectUnit, options);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return modifiedProjectUnit;
	}
	
	public ProjectUnit openProject(int userId, int projectId) throws Exception {
		ProjectUnit projectUnit = dao.openProject(projectId);

		// Call nature's createProject, which may or may not updated projectUnit
		// nature's createProject should be responsible for creating the file system representation
		IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
		ProjectUnit modifiedProjectUnit = null;
		try {
			modifiedProjectUnit = project.openProject(projectUnit);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return modifiedProjectUnit;
	}
	
	public ProjectUnit getProject(int userId, int projectId) throws Exception {
		// Check access
		boolean hasAccess = dao.checkAccess(userId, projectId);

		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			projectUnit.addSourcePath("src");
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			ProjectUnit modifiedProjectUnit = null;
			try {
				modifiedProjectUnit = project.getProject(projectUnit);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			return modifiedProjectUnit;
		}
		
		throw new Exception("No access");
	}
	
	public List<ProjectUnit> getProjects(int userId) throws Exception {
		List<ProjectUnit> projects = new ArrayList<ProjectUnit>();
		List<Integer> projectIDs = dao.getProjects(userId);
		
		for (Integer i : projectIDs) {
			projects.add(getProject(userId, i));
		}
		
		return projects;
	}
	
	public FileUnit openFile(int userId, int projectId, String path) throws Exception {
		// Check access
		boolean hasAccess = dao.checkAccess(userId, projectId);
		FileUnit file = null;

		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			file = project.openFile(projectUnit, path);
		}
		
		return file;
	}
	
	public ProjectUnit closeProject(int userId, int projectId) {
		return null;
	}
	
	public void addFolder(int userId, int projectId, String folderName, String path) throws Exception {
		boolean hasAccess = dao.checkAccess(userId, projectId);

		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			try {
				project.addFolder(projectUnit, folderName, path);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			return;
		}
		
		throw new Exception("No access");
	}
	
	public void addFile(int userId, int projectId, String fileName, String path) throws Exception {
		boolean hasAccess = dao.checkAccess(userId, projectId);

		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			try {
				project.addFile(projectUnit, fileName, path);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			return;
		}
		
		throw new Exception("No access");
	}
	
	public void addClass(int userId, int projectId, String className, String packageName) throws Exception {
		boolean hasAccess = dao.checkAccess(userId, projectId);

		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			try {
				project.addClass(projectUnit, className, packageName);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			return;
		}
		
		throw new Exception("No access");
	}
	
	public void addInterface(int userId, int projectId, String interfaceName, String packageName) throws Exception {
		boolean hasAccess = dao.checkAccess(userId, projectId);

		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			try {
				project.addInterface(projectUnit, interfaceName, packageName);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			return;
		}
		
		throw new Exception("No access");
	}
	
	public void addEnum(int userId, int projectId, String className, String packageName) throws Exception {
		boolean hasAccess = dao.checkAccess(userId, projectId);

		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			try {
				project.addEnum(projectUnit, className, packageName);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			return;
		}
		
		throw new Exception("No access");
	}
	
	public FileUnit saveFile(int userId, int projectId, String filePath, String contents) throws Exception {
		boolean hasAccess = dao.checkAccess(userId, projectId);
		
		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			FileUnit saveFile = project.saveFile(projectUnit, filePath, contents);
			
			return saveFile;
		}
		
		return null;
	}
	
	public Process runProject(int userId, int projectId, int port) throws Exception{
		
		boolean hasAccess = dao.checkAccess(userId, projectId);
		
		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			Compiler compiler = new Compiler();
				IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
				Process p = project.run(projectUnit, port);
				return p;
		}
		
		return null;
	}
	
	public void deployProject(int userId, int projectId, String email, String password, String applicationId, String proxy) throws Exception {
		boolean hasAccess = dao.checkAccess(userId, projectId);
		
		if (hasAccess == true) {
			ProjectUnit projectUnit = dao.getProject(projectId);
			IProjectNature project = (IProjectNature) Class.forName(projectUnit.getNature()).newInstance();
			AppEngineProject aep = new AppEngineProject();
			aep.deploy(projectUnit, email, password, applicationId, proxy);
		}
	}
}